/*
 * Copied from Gentyref project http://code.google.com/p/gentyref/
 * Reformatted and moved to fit package structure
 */

package com.vercer.engine.persist.util.generic;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * Wrapper around {@link Type}.
 *
 * You can use this to create instances of Type for a type known at compile
 * time.
 *
 * For example, to get the Type that represents List&lt;String&gt;:
 * <code>Type listOfString = new TypeToken&lt;List&lt;String&gt;&gt;(){}.getType();</code>
 *
 * @author Wouter Coekaerts <wouter@coekaerts.be>
 *
 * @param <T>
 *            The type represented by this TypeToken.
 */
public abstract class TypeToken<T>
{
	private final Type type;

	/**
	 * Constructs a type token.
	 */
	protected TypeToken()
	{
		this.type = extractType();
	}

	private TypeToken(Type type)
	{
		this.type = type;
	}

	public Type getType()
	{
		return type;
	}

	private Type extractType()
	{
		Type t = getClass().getGenericSuperclass();
		if (!(t instanceof ParameterizedType))
		{
			throw new RuntimeException("Invalid TypeToken; must specify type parameters");
		}
		ParameterizedType pt = (ParameterizedType) t;
		if (pt.getRawType() != TypeToken.class)
		{
			throw new RuntimeException("Invalid TypeToken; must directly extend TypeToken");
		}
		return pt.getActualTypeArguments()[0];
	}

	/**
	 * Gets type token for the given {@code Class} instance.
	 */
	public static <T> TypeToken<T> get(Class<T> type)
	{
		return new SimpleTypeToken<T>(type);
	}

	/**
	 * Gets type token for the given {@code Type} instance.
	 */
	public static TypeToken<?> get(Type type)
	{
		return new SimpleTypeToken<Object>(type);
	}

	private static class SimpleTypeToken<T> extends TypeToken<T>
	{
		public SimpleTypeToken(Type type)
		{
			super(type);
		}
	}

	@Override
	public boolean equals(Object obj)
	{
		return (obj instanceof TypeToken<?>) && type.equals(((TypeToken<?>) obj).type);
	}

	@Override
	public int hashCode()
	{
		return type.hashCode();
	}
}
